一:Mybatis连接池与实务深入

在这里插入图片描述

1.Mybatis连接池的分类

连接池作为可以减少我们获取连接所消耗的时间的技术。
Mybatis采用的是自己的连接池技术,通过主配置文件SqlMapConfig.xml中的dataSource标签type属性就是表示采用何种连接池方式。

<!--            配置数据源(连接池)-->
<dataSource type="POOLED">
<!-- 配置连接数据库的四个基本信息-->
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>

type属性的取值:

POOLED 采用传统的javax.sql.DataSource规范中的连接池,mybatis中有针对规范的实现
UNPOOLED 采用传统的获取连接的方式,虽然也实现Javax.sql.DataSource接口,但是并没有使用池的思想在这里插入图片描述
JNDI 采用服务器提供的JNDI技术实现,来获取DataSource对象,不同的服务器所能拿到DataSource是不一样。

注意:如果不是web或者maven的war工程,是不能使用的。我们课程中使用的是tomcat服务器,采用连接池就是dbcp连接池。**一般我们采用POOLED的方式**,很多时候我们所说的数据源就是为了更好的管理数据库连接,也就是我们所说的连接池技术。

2.源码分析:

(1)Mybatis 中 DataSource 的存取
MyBatis 是通过工厂模式(factory)来创建数据源 DataSource 对象的,MyBatis 创建了 DataSource 实例后,会将其放到 Configuration对象内的 Environment 对象中, 供以后使用。
(2)Mybatis 中连接的获取过程分析
创建 SqlSession 对象并需要执行 SQL 语句时,这时候 MyBatis 才会去调用 dataSource 对象来创建java.sql.Connection对象
只有在要用到的时候,才去获取并打开连接,当我们用完了就再立即将数据库连接归还到连接池中
在这里插入图片描述
在这里插入图片描述

3.Mybatis的事务控制:

JDBC 中事务可以通过手动方式将事务的提交改为手动方式,通过 setAutoCommit()方法就可以开启事务,然后它是通过sqlsession对象的commit方法和rollback方法实现事务的提交和回滚

所以由于Mybatis底层也是使用Connection,所以Mybatis事务提交方法底层也是使用JDBC的setAutoCommit()方法和rollback() commit()

/**
* 用于在测试方法之后执行
* @throws Exception
*/
@After
public void destory() throws Exception{
//提交事务
sqlSession.commit();

//6.释放资源
sqlSession.close();
in.close();
}

之前的 CUD 操作过程中,我们都要手动进行事务的提交,原因是 setAutoCommit()方法,在执行时它的值被设置为 false 了,所以我们在 CUD 操作中,必须通过 sqlSession.commit()方法来执行提交操作。

CUD 过程中必须使用 sqlSession.commit()提交事务?主要原因就是在连接池中取出的连接,都会将调connection.setAutoCommit(false)方法,这样我们就必须使用 sqlSession.commit()方法,相当于使用了 JDBC 中的 connection.commit()方法实现事务提交。明白这一点后,我们现在一起尝试不进行手动提交,一样实现 CUD 操作。

使用factory.openSession(true); 此时事务就设置为自动提交了,但是分析知道这种方法只能提交一份。
在这里插入图片描述

/**
* 用于在测试方法之前执行
* @throws Exception
*/
@Before
public void init() throws Exception{
//1.读取环境里面的总体配置文件(里面参数)
in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2.创建SqlSessionFactory工厂
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(in);
//3.使用工厂生产一个SqlSession对象(true为提交一个数据时才自动提交事务)
sqlSession = factory.openSession(true);
//4.使用SqlSession创建Dao接口的代理对象
userDao = sqlSession.getMapper(IUserDao.class);
}
/**
* 用于在测试方法之后执行
* @throws Exception
*/
@After
public void destory() throws Exception{
//提交事务
//sqlSession.commit();

//6.释放资源
sqlSession.close();
in.close();
}

虽然这也是一种方式,但就编程而言,设置为自动提交方式为 false再根据情况决定是否进行提交,即这种方式更常用。

sqlSession = factory.openSession();
sqlSession.commit();
//6.释放资源
sqlSession.close();
in.close();

二:Mybatis 的动态SQL语句

Mybatis 的映射文件中,前面我们的 SQL 都是比较简单的,有些时候业务逻辑复杂时,我们的 SQL 是动态变化的,此时在前面的学习中我们的 SQL 就不能满足要求了。

(1)if 标签

标签的 test 属性中写的是对象的属性名(User里面的),如果是包装类的对象要使用 OGNL 表达式的写法。另外要注意 where 1=1 的作用~!

<select id="findUserByCondition" resultMap="userMap" parameterType="user">
select * from user where 1 = 1
<if test="userName != null">
and username = #{userName}
</if>
<if test="userSex != null">
and sex = #{userSex}
</if>
</select>

(2)where 标签

简化上面 where 1=1 的条件拼装,使得SQL语句看起来简单,我们可以采用标签来简化开发。

<select id="findUserByCondition" resultMap="userMap" parameterType="user">
select * from user
<where>
<if test="userName != null">
and username = #{userName}
</if>
<if test="userSex != null">
and sex = #{userSex}
</if>
</where>
</select>

(3)foreach 标签

传入多个 id 查询用户信息,我们在进行范围查询时,就要将一个集合中的值,作为参数动态添加进来。
SQL 语句:
select 字段 from user where id in (?)
foreach 标签用于遍历集合,它的属性:

foreach标签的属性:

< foreach collection=”ids” open=”and id in (“ close=”)” item = “userId” separator=”,” >

collection:代表要遍历的集合元素,注意编写时不要写#{}
open:代表语句的开始部分
close:代表结束部分

item:代表遍历集合的每个元素,生成的变量名
sperator:代表分隔符,

<!--    根据queryvo中的Id集合实现查询用户列表-->
<select id="findUserInIds" resultMap="userMap" parameterType="queryvo">
select * from user
<where>
<if test="ids != null and ids.size() > 0">
<foreach collection="ids" open="and id in (" close=")" item = "userId" separator=",">
#{userId}
</foreach>
</if>
</where>
</select>
</mapper>

三:Mybatis多表查询之 一对(一)多

1. 一对一查询:

方法一:(定义AccountUser来输出)

(1)定义账户信息的实体类():Account :id,uid(对应用户的id),money

public class Account implements Serializable {

private Integer id;
private Integer uid;
private Double money;

(2)编写SQL语句:select account.,user.username, user.address *from account, user** where account.uid = user.id
(查询所有的账号表中的信息和用户表中的用户名和地址,当账号对应的用户id与用户表中user对应)

(3)定义 AccountUser 类:为了能够封装上面 SQL 语句的查询结果(账号所有信息和用户的用户名和地址),定义 AccountUser 类中要包含账户信息同时还要包含用户信息,所以我们要在定义 AccountUser 类时可以继承 Account 类

public class AccountUser extends Account {

private String username;
private String address;

(4)定义层账户的持久层Dao接口:

public interface IAccountDao {

/**
* 查询所有商品,同时获取到当前帐户的所属用户信息
* @return
*/
List<Account> findAll();
}

(5)定义 IAccountDao.xml 文件中的查询配置信息:

<select id="findAllAccout" resultType="accountuser">
select a.*,u.username,u.address from account a , user u where u.id = a.uid
</select>

注意:因为上面查询的结果中包含了账户信息同时还包含了用户信息,所以我们的返回值类型 returnType
的值设置为 AccountUser 类型,这样就可以接收账户信息和用户信息了。

(6)创建 AccountTest 测试类执行即可。

总结:定义专门的 AccountUser 类作为输出类型,其中定义了 sql 查询结果集所有的字段。此方法较为简单,企业中使用普遍。

方法二:(resultType为Account来输出)以后最常用

账户实体类(Account)中包含用户

<!--    定义封装account和user的resultMap-->
<resultMap id="accountUserMap" type="account">
<id property="id" column="aid"></id>
<result property="uid" column="uid"></result>
<result property="money" column="money"></result>
<!-- 一对一的关系映射:配置封装user的内容-->
<association property="user" column="uid" javaType="user">
<id property="id" column="id"></id>
<result property="username" column="username"></result>
<result property="address" column="address"></result>
<result property="sex" column="sex"></result>
<result property="birthday" column="birthday"></result>
</association>
</resultMap>

(1)修改账号实体类(Account):

public class Account implements Serializable {

private Integer id;
private Integer uid;
private Double money;

//从表实体应该包含一个主表实体的对象引用
private User user;

(2)重新定义 IAccountDao.xml 文件 (重新写resultMap)

<!--    定义封装account和user的resultMap-->
<resultMap id="accountUserMap" type="account">
<id property="id" column="aid"></id>
<result property="uid" column="uid"></result>
<result property="money" column="money"></result>
<!-- 它是用于指定从表方的引用实体属性的,配置封装user的内容-->
<association property="user" javaType="user">
<id property="id" column="id"></id>
<result property="username" column="username"></result>
<result property="address" column="address"></result>
<result property="sex" column="sex"></result>
<result property="birthday" column="birthday"></result>
</association>
</resultMap>

2. 一对多查询:(resultMap)

用户信息和他的账户信息为一对多关系(用户多个账号的查询),并且查询过程中如果用户没有账户信息,此时也要将用户信息查询出来,我们想到了左外连接查询比较合适。
(1)编写 SQL 语句:
select u.,a.id as aid,a.uid,a.money *from account a, user u** where u.id = a.id;
(查询所有用户表和账户的id 用户id money,从用户和账号表,当用户中id = 账号的id)

(2)User类加入List< Account> (多个账号)

public class User implements Serializable {

private Integer id;
private String username;
private String address;
private String sex;
private Date Birthday;
//一对多的关系映射,主表实体应该包含从表实体的集合引用
private List<Account> accounts;

(3) 用户持久层 Dao 映射文件配置(IUserDao.xml)

<resultMap type="user" id="userMap">
<id column="id" property="id"></id>
<result column="username" property="username"/>
<result column="address" property="address"/>
<result column="sex" property="sex"/>
<result column="birthday" property="birthday"/>

<!-- collection 是用于建立一对多中集合属性的对应关系ofType 用于指定集合元素的数据类型-->
<collection property="accounts" ofType="account">
<id column="aid" property="id"/>
<result column="uid" property="uid"/>
<result column="money" property="money"/>
</collection>
</resultMap>

<!-- 配置查询所有操作 -->
<select id="findAll" resultMap="userMap">
select account.*,user.username, user.address from account, user where account.uid = user.id
</select>

(4)编写测试方法即可

四:Mybatis多表查询之 多对多

1.实现 Role 到 User 多对多

用户与角色的关系模型:
一个用户可以有多个角色,同理:一个角色可以赋予多个用户在这里插入图片描述
(1) 业务要求及实现 SQL:

select u.*,r.id as rid,r.role_name,r.role_desc from role r left outer join user_role ur  on r.id = ur.rid left outer join user u on u.id = ur.uid

(2)编写角色实体类(Role):

public class Role implements Serializable {

private Integer roleId;
private String roleName;
private String roleDesc;

//多对多的关系映射:一个角色可以赋予多个用户
private List<User> users;

(3)编写 Role 持久层映射配置文件(IRoleDao.xml):
resultMap为角色,里面有user

<!--定义role表的ResultMap-->
<resultMap id="roleMap" type="role">
<id property="roleId" column="rid"></id>
<result property="roleName" column="role_name"></result>
<result property="roleDesc" column="role_desc"></result>
<collection property="users" ofType="user">
<id column="id" property="id"></id>
<result column="username" property="username"></result>
<result column="address" property="address"></result>
<result column="sex" property="sex"></result>
<result column="birthday" property="birthday"></result>
</collection>
</resultMap>

<!--查询所有-->
<select id="findAll" resultMap="roleMap">
select u.*,r.id as rid,r.role_name,r.role_desc from role r left outer join user_role ur on r.id = ur.rid left outer join user u on u.id = ur.uid
</select>

(4)最后编写测试类

2.实现 User 到 Role 的多对多

一个用户可以具有多个角色,这样用户到角色的关系也还是一对多关系。这样我们就可以认为 User 与 Role 的多对多关系,可以被拆解成两个一对多关系来实现。

后面的SQL语句,角色实体类, 持久层映射配置文件同理。